#ifndef __M_BINGDIN_H__
#define __M_BINGDIN_H__
#include"../mqcommon/mq_helper.hpp"
#include"../mqcommon/mq_logger.hpp"
#include"../mqcommon/mq_msg.pb.h"
#include<string>
#include<iostream>
#include<unordered_map>
#include<memory>

namespace bitmq{
    
    class Bingding{
        public:
        std::string _exchange_name;//交换机名称
        std::string _queue_name;//队列名称
        std::string _bingding_key;//绑定密钥
        public:
        using ptr=std::shared_ptr<Bingding>;
        Bingding(){}
        Bingding(const std::string& ename,const std::string& qname,const std::string& bd_key):_exchange_name(ename),
        _queue_name(qname),_bingding_key(bd_key){}
    };
    //队列与绑定一一对应
     using QueueBingdingMap=std::unordered_map<std::string,Bingding::ptr>;

     //一个交换机可以与多个队列对应
     using BingdingMap=std::unordered_map<std::string,QueueBingdingMap>;
    class BingdingMapper{
        public:
        BingdingMapper(const std::string& dbfile):_db(dbfile){
            std::string path =bitmq::FileHelper::parentDirectory(dbfile);
            bitmq::FileHelper::createDirectory(path);
            assert(_db.open());
            createTable();
        }
        void createTable()//创建持久化数据表
        {
            std::string sql="create table if not exists bingding_table(exchange_name varchar(32),queue_name varchar(32),bingding_key varchar(128));";
            bool ret=_db.exec(sql.c_str(),nullptr,nullptr);
            if(!ret)
            {
                ELOG("创建绑定信息持久化数据表失败！");
                abort();
            }
        }
        void removeTable()//删除持久化数据表
        {
            #define DROP_TABLE "drop table if exists bingding_table"
            bool ret=_db.exec(DROP_TABLE,nullptr,nullptr);
            if(!ret)
            {
                ELOG("删除绑定信息持久化数据表失败！");
                abort();
            }

        }
        bool insert(Bingding::ptr bd)//插入绑定关系数据
        {
            std::stringstream ss;
            ss<<"insert into bingding_table values(";
            ss<<"'"<<bd->_exchange_name<<"',";
            ss<<"'"<<bd->_queue_name<<"',";
            ss<<"'"<<bd->_bingding_key<<"');";
            bool ret=_db.exec(ss.str(),nullptr,nullptr);
            if(!ret)
            {
                ELOG("插入绑定信息持久化数据失败！");
                return false;
            }
            return true;
        }
        void remove(const std::string&ename,const std::string&qname)//移除一对绑定
        {
            std::stringstream ss;
            ss<<"delete from bingding_table where exchange_name = ";
            ss<<"'"<<ename<<"' and queue_name = ";
            ss<<"'"<<qname<<"';";
            bool ret=_db.exec(ss.str(),nullptr,nullptr);
            if(!ret)
            {
                ELOG("移除一对绑定关系失败！");
                abort();
            }
        }
        void removeExchangeBingding(const std::string&ename)//移除交换机相关绑定数据
        {
            std::stringstream ss;
            ss<<"delete from bingding_table where exchange_name=";
            ss<<"'"<<ename<<"';";
            _db.exec(ss.str(),nullptr,nullptr);
        }
        void removeQueueBingding(const std::string&qname)//移除队列相关的绑定数据
        {
            std::stringstream ss;
            ss<<"delete from bingding_table where queue_name=";
            ss<<"'"<<qname<<"';";
            _db.exec(ss.str(),nullptr,nullptr);
        }
        BingdingMap recovery()//将数据恢复到内存中
        {
            BingdingMap bdmap;
            std::string sql="select exchange_name,queue_name,bingding_key from bingding_table;";
            _db.exec(sql,selectCallBack,&bdmap);
            return bdmap; 
        }
        private:
        static int selectCallBack(void*arg,int col,char**row,char**fields)
        {
            BingdingMap* bdmap=(BingdingMap*)arg;
            Bingding::ptr bd=std::make_shared<Bingding>(row[0],row[1],row[2]);

            //为了保证交换机绑定，已经存在，创建新的绑定对象，插入，会覆盖丢原有的数据
            //因此，要获取交换机映射对象，进行添加数据
            //但如果交换机映射对象不存在，这时就要用引用（保证的交换机不存在，就自动创建）
            QueueBingdingMap &qbmap=(*bdmap)[bd->_exchange_name];
            bdmap->insert(std::make_pair(bd->_exchange_name,qbmap));

            return 0;
        }
        private:
        Sqlite _db;//sqlite3库操作句柄
    };
    //绑定关系的内存管理类
    class BingdingManager
    {
        public:
        using ptr=std::shared_ptr<BingdingManager>;
        BingdingManager( std::string dbfile):_bmap(dbfile){
            _bdmap=_bmap.recovery();
        }
        //添加绑定
        bool bind(const std::string&ename,const std::string&qname,const std::string&key,bool durable)
        {
            //加锁，构造对象，添加
            std::unique_lock<std::mutex> lock(_mutex);
            auto it=_bdmap.find(ename);
            if(it!=_bdmap.end()&&it->second.find(qname)!=it->second.end())//只存在交换机或者两者都不存在，都不会进入
            {
                return true;
            }
            Bingding::ptr bd=std::make_shared<Bingding>(ename,qname,key);
            //是否将绑定信息持久化，要根据交换机和队列是否持久化来，决定的
            if(durable)
            {
                bool ret=_bmap.insert(bd);
                if(!ret) return false;
            }
            auto&map=_bdmap[bd->_exchange_name];//存在交换机就获取，如果不存在交换机就获取
            map.insert(std::make_pair(bd->_queue_name,bd));
            return true;
        }
        //解除绑定
        void unbind(const std::string&ename,const std::string&qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit=_bdmap.find(ename);
            if(eit==_bdmap.end())//没找到交换机相关的绑定信息
            {
                return;
            }
            auto qit=_bdmap[ename].find(qname);
            if(qit==_bdmap[ename].end())//没找队列相关的绑定信息
            {
                return;
            }
            _bmap.remove(ename,qname);
            _bdmap[ename].erase(qname);
        }
        //移除交换机相关的绑定信息
        void removeExchangeBingdings(const std::string&ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _bmap.removeExchangeBingding(ename);
            _bdmap.erase(ename);
        }
        //移除队列相关的绑定信息
        void removeQueueBingdings(const std::string&qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _bmap.removeQueueBingding(qname);
            for(auto it=_bdmap.begin();it!=_bdmap.end();it++)
            {   //遍历每一个交换机，删除队列的相关的绑定信息
                it->second.erase(qname);   
            }
        }
        //获取指定交换机的所有绑定信息
        QueueBingdingMap getExchangeBingdings(const std::string&ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit=_bdmap.find(ename);
            if(eit==_bdmap.end())//没找到交换机的相关绑定信息
            {
                return QueueBingdingMap();
            }
            return eit->second;
        }
        //获取指定队列的绑定信息
        Bingding::ptr getQueueBingdings(const std::string&ename,const std::string&qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit=_bdmap.find(ename);
            if(eit==_bdmap.end())//没找到交换机的相关绑定信息
            {
                return Bingding::ptr();
            }
            auto qit=_bdmap[ename].find(qname);
            if(qit==_bdmap[ename].end())//没找到队列相关的绑定信息
            {
                return Bingding::ptr();
            }
            return qit->second;
        }
        //获取所有队列绑定信息的个数
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            size_t tatol_size=0;
            for(auto start=_bdmap.begin();start!=_bdmap.end();start++)
            {
                tatol_size+=start->second.size();//将所有队列个数累加
            }
            return tatol_size;
        }
        //判断队列绑定信息是否存在
        bool exists(const std::string&ename,const std::string&qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit=_bdmap.find(ename);
            if(eit==_bdmap.end())return false;

            auto qit=eit->second.find(qname);
            if(qit==eit->second.end())return false;
            return true;
        }
        //清理所有绑定数据，包括持久化数据
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _bmap.removeTable();
            _bdmap.clear();
        }
        private:
        std::mutex _mutex;//加锁
        BingdingMapper _bmap;//映射
        BingdingMap _bdmap;//交换机与所有绑定信息的对应关系
    };
}
#endif